﻿#pragma execution_character_set("utf-8")
#include <stdexcept>
#include "TheTask.h"
#include "gdal_priv.h"
#include <vector>
using std::vector;
using std::runtime_error;

TheTask::TheTask(string infile, string outfile, double percent,double vmin,double vmax)
{
	_infile = infile;
	_outfile = outfile;
	_percent = percent*0.01 ;
	_vmin = vmin;
	_vmax = vmax;
	if (_percent < 0.01) _percent = 0.01;
	if (_percent > 0.90) _percent = 0.90;
}
void TheTask::run()
{
	string error;
	GDALDataset* inds = 0;
	GDALDataset* outds = 0;
	try {
		inds = (GDALDataset*)GDALOpen(_infile.c_str(), GA_ReadOnly);
		if (inds == 0) {
			throw runtime_error("打开输入文件失败。");
		}
		int xsize = inds->GetRasterXSize();
		int ysize = inds->GetRasterYSize();
		int nbands = inds->GetRasterCount();
		if (xsize == 0 || ysize == 0 || nbands == 0) {
			throw runtime_error("xsize, ysize, nbands其中一个为0。");
		}
		GDALDataType dtype = inds->GetRasterBand(1)->GetRasterDataType();
		GDALDriver* driver = GetGDALDriverManager()->GetDriverByName("GTiff");
		outds = driver->Create(_outfile.c_str(),xsize,ysize,nbands, dtype ,0);

		double trans[6];
		inds->GetGeoTransform(trans);
		outds->SetGeoTransform(trans);
		outds->SetProjection(inds->GetProjectionRef());

		int allbandrows = nbands * ysize;
		for (int iband = 0; iband < nbands; ++iband) {
			doOneBand(inds, outds, iband, xsize, ysize, allbandrows,_vmin,_vmax);
		}

		if (inds) GDALClose(inds); inds = 0;
		if (outds) GDALClose(outds); outds = 0;
		this->notifyDone();
		emit onSaveTaskDone();
	}
	catch (runtime_error& ex) {
		if (inds) GDALClose(inds); inds = 0;
		if (outds) GDALClose(outds); outds = 0;
		error = ex.what() ;
		this->notifyError(error);
	}
}

void TheTask::doOneBand(GDALDataset* inds, GDALDataset* outds, int iband0b,
	const int xsize, const int ysize, const int allbandrows,
	double vmin,double vmax)
{
	const int thumbSize = 100;
	vector<float> thumbdata(thumbSize * thumbSize);
	inds->GetRasterBand(iband0b + 1)->RasterIO(GF_Read, 0, 0, xsize, ysize, thumbdata.data(),
		thumbSize, thumbSize, GDT_Float32, 0, 0, 0);
	double valmin = 9999999;
	double valmax = -9999999;
	int thumbGoodPixelCnt = 0;
	for (int it = 0; it < thumbdata.size() ; ++it ) {
		double tval = thumbdata[it];
		if ( vmin<=tval && tval<=vmax ) {
			valmin = fmin(valmin, tval);
			valmax = fmax(valmax, tval);
			thumbGoodPixelCnt++;
		}
	}
	if (valmin >= valmax) return;//bad min/max
	double step = (valmax - valmin) / 100;
	int thumbDarkNum = thumbGoodPixelCnt * _percent;
	vector<int> bin100(100, 0);
	for (int it = 0; it < thumbdata.size(); ++it) {
		double tval = thumbdata[it];
		if (vmin <= tval && tval <= vmax) {
			int index = (tval - valmin) / step;
			index = fmin(index, 99);
			bin100[index] ++;
		}
	}
	int currSum = 0;
	double darkValue = 0;
	for (int i = 0; i < 100; ++i) {
		currSum += bin100[i];
		if (currSum > thumbDarkNum) {
			darkValue = valmin + i * step;
			break;
		}
	}
	//processing
	int per0 = -1;
	int baseY = iband0b * ysize + 1;
	double perScale = 100.0 / allbandrows;
	vector<float> rowdata(xsize);
	for (int iy = 0; iy < ysize; ++iy) {
		inds->GetRasterBand(iband0b + 1)->RasterIO(GF_Read, 0, iy, xsize, 1, rowdata.data(), xsize, 1, GDT_Float32, 0, 0, 0);
		for (int ix = 0; ix < xsize; ++ix) {
			double tval = rowdata[ix];
			if (vmin <= tval && tval <= vmax) {
				tval -= darkValue;
				tval = fmax(tval, 0);
				rowdata[ix] = tval;
			}
		}
		outds->GetRasterBand(iband0b + 1)->RasterIO(GF_Write, 0, iy, xsize, 1, rowdata.data(), xsize, 1, GDT_Float32, 0, 0, 0);
		int per1 = (baseY + iy) * perScale;
		if (per1 != per0) {
			per0 = per1;
			this->notifyProgress(per0, 100);
		}
	}
}
